package durst.data.dao.impl;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import com.j256.ormlite.dao.BaseDaoImpl;
import com.j256.ormlite.dao.GenericRawResults;
import com.j256.ormlite.field.DataType;
import com.j256.ormlite.stmt.PreparedDelete;
import com.j256.ormlite.stmt.PreparedUpdate;
import com.j256.ormlite.support.ConnectionSource;
import com.j256.ormlite.table.DatabaseTableConfig;

import durst.data.Account;
import durst.data.Event;
import durst.data.Event.RepeatFrequency;
import durst.data.Invite;
import durst.data.UserCalendar;
import durst.data.UserCalendar.SharedStatus;

public class InviteDao<T, ID> extends BaseDaoImpl<T, ID> {
	@SuppressWarnings("unchecked")
	public InviteDao(ConnectionSource connectionSource) throws SQLException {
		super(connectionSource, (Class<T>) Invite.class);
	}

	public InviteDao(ConnectionSource connectionSource, DatabaseTableConfig<T> tableConfig)
			throws SQLException {
		super(connectionSource, tableConfig);
	}

	public List<UserCalendar> getInvitedCalendars(Account acc, Date start, boolean jumplistOnly)
			throws SQLException {
		java.sql.Date sql = new java.sql.Date(start.getTime());
		List<UserCalendar> ret = new LinkedList<UserCalendar>();
		GenericRawResults<Object[]> res = queryRaw(
				"SELECT DISTINCT cal.* FROM Invite inv "
						+ "JOIN Event ent ON ent.id = inv.event_id AND (DATEDIFF(?, ent.endTime) < 0 OR ent.repeated = 1 AND DATEDIFF(?, ent.doNotRepeatPast) < 0) "
						+ "JOIN UserCalendar cal ON cal.id = ent.parent_id "
						+ "WHERE inv.invitee_id = ?"
						+ (jumplistOnly ? " AND inv.jumplist = 1 " : ""), new DataType[] {
						DataType.INTEGER, DataType.STRING, DataType.ENUM_STRING, DataType.INTEGER,
						DataType.BOOLEAN, DataType.BOOLEAN }, sql.toString(), sql.toString(),
				Integer.toString(acc.getId()));

		for (Object[] row : res) {
			Account owner = new Account();
			owner.setId((Integer) row[3]);
			UserCalendar cal = new UserCalendar((String) row[1],
					SharedStatus.valueOf((String) row[2]), owner, (Boolean) row[4],
					(Boolean) row[5]);
			cal.setId((Integer) row[0]);

			ret.add(cal);
		}

		return ret;
	}

	@SuppressWarnings("unchecked")
	public void jumplist(Account acc, List<Event> events, boolean jump) throws SQLException {
		List<Integer> ids = new ArrayList<Integer>(events.size());
		for (Event ent : events)
			ids.add(ent.getId());
		update((PreparedUpdate<T>) updateBuilder().updateColumnValue(Invite.JUMPLIST, jump).where()
				.eq(Invite.INVITEE, acc.getId()).and().in(Invite.EVENT, ids).prepare());
	}

	@SuppressWarnings("unchecked")
	public void declineInvites(Account acc, List<Invite> invites) throws SQLException {
		Set<Integer> events = new HashSet<Integer>(invites.size());

		for (Invite inv : invites) {
			events.add(inv.getEvent().getId());
		}

		delete((PreparedDelete<T>) deleteBuilder().where().eq(Invite.INVITEE, acc.getId()).and()
				.in(Invite.EVENT, events).prepare());
	}

	@SuppressWarnings("unchecked")
	public void revokeInvites(Account requester, List<Invite> invites) throws SQLException {
		List<ID> ids = new ArrayList<ID>(invites.size());

		for (Invite inv : invites) {
			for (Object[] row : queryRaw("SELECT inv.id FROM Invite inv "
					+ "JOIN Event ent ON inv.event_id = ent.id "
					+ "JOIN UserCalendar cal ON cal.id = ent.parent_id "
					+ "WHERE inv.event_id = ? AND inv.invitee_id = ? AND cal.owner_id = ?",
					new DataType[] { DataType.INTEGER }, Integer.toString(inv.getEvent().getId()),
					Integer.toString(inv.getInvitee().getId()), Integer.toString(requester.getId())))
				ids.add((ID) row[0]);
		}

		if (ids.size() > 0)
			delete((PreparedDelete<T>) deleteBuilder().where().in("id", ids).prepare());
	}

	public List<Event> getInvites(Account acc, UserCalendar cal) throws SQLException {
		List<Event> ret = new LinkedList<Event>();
		for (Object[] row : queryRaw("SELECT ent.* FROM Invite inv "
				+ "JOIN Event ent ON inv.event_id = ent.id "
				+ "JOIN UserCalendar cal ON cal.id = ent.parent_id "
				+ "WHERE cal.id = ? AND inv.invitee_id = ?", new DataType[] { DataType.INTEGER,
				DataType.INTEGER, DataType.STRING, DataType.DATE, DataType.DATE, DataType.BOOLEAN,
			    DataType.ENUM_STRING, DataType.DATE, DataType.STRING },
				Integer.toString(cal.getId()), Integer.toString(acc.getId()))) {
			UserCalendar parent = new UserCalendar();
			parent.setId((Integer) row[1]);

			ret.add(new Event((Integer) row[0], parent, (String) row[2], (Date) row[3],
					(Date) row[4], (Boolean) row[5], (Date) row[7],
					RepeatFrequency.valueOf((String) row[6]), (String) row[8]));
		}
		
		return ret;
	}
}
